WIP: Add GitLab repository workspace support#1547
Draft
simple-agent-manager[bot] wants to merge 17 commits into
Draft
WIP: Add GitLab repository workspace support#1547simple-agent-manager[bot] wants to merge 17 commits into
simple-agent-manager[bot] wants to merge 17 commits into
Conversation
Contributor
…into sam/gitlab-repo-workspace-wip # Conflicts: # apps/api/src/auth.ts # apps/api/src/routes/mcp/instruction-tools.ts # apps/web/tests/playwright/project-onboarding-wizard-audit.spec.ts # packages/vm-agent/internal/server/server.go # packages/vm-agent/internal/server/workspaces.go
Re-ran all 8 /do Phase 5 local reviewers (prior session could not launch them). Applied in-scope fixes: - Add direct GitLab POST /api/projects route behavioral test (9 scenarios: happy/400/401/403/404, non-default branch exist/absent, 409 duplicate, sidecar-insert rollback). - Bound every GitLab API call with a configurable timeout (GITLAB_API_TIMEOUT_MS, default 30s) via shared fetchWithTimeout — Principle XI (no unbounded fetch on the core project-creation path). - Fix VM credential path gate fail-open: isAllowedCredentialPathForWorkspace now falls back to process config RepoProvider/RepositoryPath for unregistered workspaces (mirrors the host gate; preserves standalone mode) instead of returning true. Adds a regression test. - Add verifyGitLabProjectAccess error-branch tests (<Developer 403, 404 not-found, group-access inheritance). - Fix Playwright onboarding audit /api/gitlab/branches mock to return a bare array matching the real route contract. - Doc sync: drop stale "future" from security.md GitLab row; document required read_user+api GitLab OAuth scopes in self-hosting.mdx and the new GITLAB_API_TIMEOUT_MS in .env.example. Deferred to later stack layers (documented in task file / PR): MR-layer host validation + dedicated external HTTP client, OAuth scope configurability, GitLabProjectSelector ARIA/keyboard polish, per-request lookup dedup. Refs idea 01KV7ZFD6HZS5N7J45VA798KN1, mission 2b4af813-b907-4033-9145-b2087078d08b. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…session_snapshots Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GitLab vends a broad, unexpiring user OAuth token (unlike GitHub's repo-scoped installation tokens), so the credential exchange must not fall back to empty-allow behavior: - Request-side gate: gitlab-bound workspaces must supply both host and path query params or the vm-agent returns 204 without ever contacting the control plane. - Response-side gate: a provider=gitlab response is withheld unless the caller supplied host+path AND the control plane resolved a non-empty repositoryPath, on top of the existing exact host/path match checks. GitHub/Artifacts keep empty-allow (gh wrapper sends host with no path). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GitLab refresh tokens are rotating and single-use — concurrent BetterAuth getAccessToken calls replay a consumed refresh token and revoke the whole token family upstream (same class as the codex staging refresh-revocation incident; .claude/rules/44 + 45). Adds GitLabUserAccessTokenLock (sibling of GitHubUserAccessTokenLock, wrangler DO migration v17) and routes all GitLab token lookups DO-first with a direct fallback only when the binding is absent. Includes a rule-45 concurrency regression test (verified to fail with the mutex bypassed) and service-level DO-first routing tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Bound tryCreateGitLabMergeRequest with GitExecTimeout context so a hung GitLab API cannot stall task completion indefinitely - Redact token material from logged GitLab error bodies via redactTaskCallbackDiagnosticText - Fail closed when a gitlab-bound workspace has an empty bound repository path (both runtime-map and config-fallback branches) - Normalize requested host casing in the git-credential helper shell script (tr lower) and Go-side allowed-host comparison (hostnames are case-insensitive per RFC 4343) - Remove dead gitrepo.HostMatches - Tests: 409->existing-MR fallback with param/auth assertions, empty-path fail-closed variants, mixed-case host acceptance + wrong-host rejection exercised through the rendered shell helper with a curl shim Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n expiry - 403 when the stored project GitLab repo binding userId does not match the workspace owner (fail-closed before any token mint or upstream verify) - Thread the GitLab access-token expiry through to the git-token response (result-object token variants; string wrappers preserve existing exports) - Warn-log when the GITLAB_USER_ACCESS_TOKEN_LOCK DO binding is absent and refresh serialization falls back to unserialized direct lookup - Delete project_gitlab_repositories rows in the project-delete D1 batch (FK cascade is not enforced for ALTER TABLE-added tables) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds merge-blocking tests identified by the test-engineer review: - git-token GitLab branch: GITLAB_REAUTH_REQUIRED propagates as 401 without re-verifying upstream access; missing repository metadata fails closed with 403 before any token is minted - getGitLabOAuthConfig returns null for every partial-config case (missing host / client id / client secret) and normalizes a host with a path down to its origin - new platform-config-validation suite covering the GitLab host rules: https required, http localhost carve-out, credential/path/query/ fragment rejection, invalid URL, and client id/secret length checks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
HIGH-1 (drop 'api' OAuth scope): REJECTED. GitLab's REST API (merge request creation, verifyGitLabProjectAccess project lookups) requires the 'api' scope — read_repository/write_repository only cover git-over-HTTP and read_api cannot create merge requests. HIGH-3 (rate limit on token vending): DEFERRED with tracked follow-up. The endpoint requires a workspace callback JWT plus the fail-closed userId binding, and the per-user DO mutex already eliminates the concurrent-refresh token-family-revocation vector. A naive per-call limit would throttle legitimate git credential fetches; the correct refresh-aware limit is scoped in tasks/backlog/2026-07-12-gitlab-token-lock-rate-limit.md. Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
|
Fixes the SonarCloud new-duplication gate on PR #1547 (3.9% > 3%): - GitHubUserAccessTokenLock and GitLabUserAccessTokenLock now extend a shared abstract UserAccessTokenLock (behavior and log keys unchanged; wrangler-bound class names preserved). - The two tryCreateGitLabMergeRequest tests share a newGitLabMRTestServer helper instead of duplicating Server/runtime construction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
22 tasks
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.





WIP / stacking
Stack layer 2 on #1545 (
sam/gitlab-platform-config-wip) at foundation SHA570f4a5090fe05e5b382b585cd2ea8be5ec0d2be.Refs durable idea
01KV7ZFD6HZS5N7J45VA798KN1, mission2b4af813-b907-4033-9145-b2087078d08b, and SAM task01KX92JQ08DTCX277PY5DYKAHS/ continuation task01KX94W93DT9Q021HM5TB24VQ8.Summary
0091_project_gitlab_repositories.sql.Foundation merge / conflicts
Normal merge commit:
e06e820b2d830361bbc7d51fceaa64bd8815afc3.Resolved five conflicts:
apps/api/src/auth.ts: kept foundation auth updates and GitLab repository scopes.apps/api/src/routes/mcp/instruction-tools.ts: kept foundation conversation/task context resolution.apps/web/tests/playwright/project-onboarding-wizard-audit.spec.ts: kept refreshed audit harness plus GitLab mocks/scenarios.packages/vm-agent/internal/server/server.go: kept foundation container git-command runner plus GitLab MR response handling.packages/vm-agent/internal/server/workspaces.go: kept foundation standalone/runtime refactor plus GitLab provider clone/host/path request metadata.The stale layer-2 migration number collided with the refreshed base's
0088–0090; it was renumbered to0091.Validation
Continuation run (2026-07-11, HEAD
c26162c4a):pnpm typecheck(api + web + shared) — pass.pnpm lint(api + web + shared) — 0 errors (pre-existing warnings only).pnpm --filter @simple-agent-manager/api test— 404/404 files, 5,922/5,922 tests pass (prior stale provider-count assertion now repaired; new GitLab route + service tests included).apps/api/tests/unit/routes/project-gitlab-creation.test.ts— 9/9 (route-level GitLabPOST /api/projectsbehavioral coverage).verifyGitLabProjectAccessservice error branches — <Developer 403, GitLab 404 → not-found, group-access inheritance.pnpm --filter @simple-agent-manager/web test -- project-onboarding— 40/40.pnpm build(shared + api) — pass.go test -race ./internal/server/... ./internal/bootstrap/... ./internal/persistence/... ./internal/config/...on Go 1.24+ — pass (incl. newTestIsAllowedCredentialPathForWorkspaceFallsBackToConfig).go vet ./internal/server/... ./internal/bootstrap/...— clean./api/gitlab/branchesbare-array mock and overflow assertions.migration-safety,do-migration-safety,file-sizes,source-contract-tests,wrangler-bindings,ast-checks— pass.main; this stacked PR targetssam/gitlab-platform-config-wip, so the main CI workflow does not register. SonarCloud + CodSpeed run and are green.Staging Verification
Intentionally skipped by explicit user instruction.
No staging environment was created, updated, deployed, or queried for verification.
Data flow trace
ProjectOnboardingWizard/GitLabProjectSelectorselects numeric GitLab project and branch.CreateProjectRequest.gitlabProjectIdcrossesPOST /api/projects.verifyGitLabProjectAccessresolves configured host, user OAuth token, Developer access, immutable numeric project ID, namespace path, clone URL, and default branch.project_gitlab_repositoriessidecar persist provider identity.requireRepositoryUserAccess/requireRepositoryOwnerAccessre-verify identity.resolveWorkspaceGitSourceloads sidecar metadata and passes provider/clone/host/path tocreateWorkspaceOnNode; missing metadata fails permanently.credential.useHttpPath=true./git-credential?host=&path=; VM calls authenticated control-plane/:id/git-token; API re-verifies exact GitLab identity before returningoauth2credentials. Verified consistent camelCase field mapping acrossnode-agent.ts→ GocreateWorkspaceRequestjson tags → sharedCreateWorkspaceAgentRequestSchema.GitLabRepoBrowserfor branches/tree/file/raw/compare.repoProvider=gitlab; GitHub PR and Artifacts paths remain unchanged.Untested gaps / explicit later layers
GitLabProjectSelectorARIA combobox + keyboard navigation + no-results / provider-switch reset polish.RepositoryHostURL validation, and a dedicated external HTTP client for GitLab MR API calls (host is trusted platform config, so this is defense-in-depth for the MR path).Specialist Review Evidence
bwrapsandbox failure; the 2026-07-11 continuation re-ran all 8 successfully.)needs-human-reviewretained — see rationale below (draft stacked layer with documented cross-layer deferrals awaiting a human decision to advance the stack; NOT because any reviewer failed to run).POST /api/projectscoverage. Addedproject-gitlab-creation.test.ts(9 scenarios).verifyGitLabProjectAccess<Developer/404/group-access tests. Verified VM create-workspace field mapping is consistent camelCase (no snake/camel mismatch).gitlabFetch. AddedGITLAB_API_TIMEOUT_MS(default 30s) via sharedfetchWithTimeout. Migration 0091 confirmed additive/safe with correct FK + indexes.RepositoryHostvalidation and dedicated external HTTP client deferred to the later MR layer (host is trusted platform config)./api/gitlab/branchesmock (bare array matching the real route). Data path GitLab→API verified complete. ARIA/keyboard/no-results/provider-reset polish deferred to the later UX layer (matches existing selector pattern).security.mdGitLab row; documented requiredread_user+apiGitLab OAuth scopes inself-hosting.mdx; documentedGITLAB_API_TIMEOUT_MSin.env.example.needs-human-reviewrationaleAll eight required reviewers ran and every in-scope CRITICAL/HIGH finding was fixed in this branch. The label is retained (not removed) because this is an intentionally unmerged, draft stacked layer that a human must review before advancing the stack, and because several findings are legitimately deferred to explicit later stack layers (MR hardening, OAuth scope configurability, onboarding UX/ARIA polish). This draft must not be marked ready or merged.
Exceptions
Agent Preflight (Required)
Classification
External References
GITLAB_API_TIMEOUT_MS.Codebase Impact Analysis
packages/shared: repository provider and VM-agent request contracts.apps/api: BetterAuth scopes, D1 migration/schema, GitLab service/routes (timeout added), project/access/token boundaries, repo browser, TaskRunner/node metadata.apps/web: provider onboarding, project/branch selector, API client, unit and visual tests (branches mock corrected).packages/vm-agent: clone normalization, credential helper (path gate fail-open fixed), host/path checks, persistence/recovery, completion/MR behavior.Documentation & Specs
tasks/active/2026-07-08-gitlab-repo-workspace-wip.mdwith the Phase 5 continuation outcomes.security.md,self-hosting.mdx, and.env.exampleupdated for GitLab repository access and the new timeout env var. Further public connection/onboarding UX documentation is deferred to the focused discovery/UX layer.Constitution & Risk Check
GITLAB_API_TIMEOUT_MS; deployment host derives from platform GitLab configuration; repo-browser limits use configured values.0091migration, foreign key cascade, project/user/host/project indexes.